/*
 * @Author: Troubadour 2276791354@qq.com
 * @Date: 2024-06-06 15:36:12
 * @LastEditors: Troubadour 2276791354@qq.com
 * @LastEditTime: 2024-06-12 15:28:15
 * @Version:
 * @Description:
 */

/*-------------------------------------------------------------------------------------------------------------------------------------
-                                                  Include file                                                                       -
--------------------------------------------------------------------------------------------------------------------------------------*/

#include "boot_user.h"
/*-------------------------------------------------------------------------------------------------------------------------------------
-                                                  Internal macro                                                                     -
--------------------------------------------------------------------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------------------------------------------------------------------
-                                                  Internal enum                                                                      -
--------------------------------------------------------------------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------------------------------------------------------------------
-                                                  Internal structure                                                                 -
--------------------------------------------------------------------------------------------------------------------------------------*/

typedef struct
{
    uint8_t boot_flag;
    uint32_t firmware_crc32;
    uint32_t firmware_size;
    uint32_t boot_data_crc;
} Boot_data_t;
/*-------------------------------------------------------------------------------------------------------------------------------------
-                                                  Internal function                                                                  -
--------------------------------------------------------------------------------------------------------------------------------------*/

typedef void (*Jump_Fun)(void);
static void Boot_ExecuteApp(uint32_t App_Addr);
static bool Boot_WirteFirmwareToFlash(uint32_t flash_addr, uint32_t firmware_addr, uint32_t data_size);
/*-------------------------------------------------------------------------------------------------------------------------------------
-                                                  Internal variables                                                                 -
--------------------------------------------------------------------------------------------------------------------------------------*/

static Boot_data_t boot_data;
static uint8_t firmware_buff[1024 * 8] = {0};
/*-------------------------------------------------------------------------------------------------------------------------------------
-                                                  External variables                                                                 -
--------------------------------------------------------------------------------------------------------------------------------------*/

extern void printfHex(uint8_t *pData, uint16_t size);
/*-------------------------------------------------------------------------------------------------------------------------------------
-                                                  Function body                                                                      -
--------------------------------------------------------------------------------------------------------------------------------------*/

/**
 * @function: MSR_MSP
 * @brief This function is used to set the MSP
 * @param {uint32_t} ulAddr
 * @return {*}
 */
__asm void MSR_MSP(uint32_t ulAddr)
{
    MSR MSP, r0 // set Main Stack value
    BX r14
}

/**
 * @function: Boot_ExecuteApp
 * @brief This function is used to execute the application
 * @param {uint32_t} App_Addr: Application address
 * @return {*}
 */
static void Boot_ExecuteApp(uint32_t App_Addr)
{
    Jump_Fun JumpToApp;

    if (((*(__IO uint32_t *)App_Addr) & 0x2FFE0000) == 0x20000000)
    {
        JumpToApp = (Jump_Fun) * (__IO uint32_t *)(App_Addr + 4);
        MSR_MSP(*(__IO uint32_t *)App_Addr);
        JumpToApp();
    }
    else
    {
        printf("There is None APP to jump,ERROR!!!\r\n");
    }
}

/**
 * @function: Boot_CRC32
 * @brief CRC32 check.
 * @param {uint8_t} *data: Data buffer.
 * @param {size_t} len: Buffer size.
 * @return {uint32_t} Return CRC32 result.
 */
static uint32_t Boot_CRC32(const uint8_t *data, size_t len)
{
    uint32_t crc = 0xFFFFFFFF;
    uint8_t byte = 0;
    while (len--)
    {
        byte = *data++;
        crc ^= byte;
        for (int j = 0; j < 8; j++)
        {
            uint32_t mask = -(crc & 1);
            crc = (crc >> 1) ^ (0xEDB88320 & mask);
        }
    }
    return ~crc;
}

/**
 * @function: Boot_ReadData
 * @brief This function is used to read the boot data from the flash.
 * @param {Boot_data_t} *p_boot_data: Boot data.
 * @return {bool} true: Read success, false: Read failed.
 */
static bool Boot_ReadData(Boot_data_t *p_boot_data)
{
    uint32_t temp_crc32 = 0;
    uint16_t read_size = sizeof(Boot_data_t) / 4;
    Boot_data_t read_boot_data;

    if (sizeof(Boot_data_t) % 4)
    {
        read_size++;
    }

    STMFLASH_Read(BOOT_DATA_ADDR, (uint32_t *)&read_boot_data, read_size);
    printfHex((uint8_t *)&read_boot_data, sizeof(Boot_data_t));

    temp_crc32 = read_boot_data.boot_data_crc;
    read_boot_data.boot_data_crc = 0xFFFFFFFF;
    read_boot_data.boot_data_crc = Boot_CRC32((const uint8_t *)&read_boot_data, sizeof(Boot_data_t));

    if (read_boot_data.boot_data_crc != temp_crc32)
    {
        printf("Read boot data CRC32 ERROR. Read crc: [0x%08X], calculate crc: [0x%08X] \r\n", temp_crc32, read_boot_data.boot_data_crc);
        return false;
    }
    read_boot_data.boot_data_crc = temp_crc32;
    *p_boot_data = read_boot_data;
    return true;
}

/**
 * @function: Boot_WriteData
 * @brief This function is used to write the boot data to the flash.
 * @param {Boot_data_t} *p_boot_data: Boot data.
 * @return {bool} true: Write success, false: Write failed.
 */
static bool Boot_WriteData(Boot_data_t *p_boot_data)
{
    Boot_data_t temp_data;
    uint16_t write_size = sizeof(Boot_data_t) / 4;

    if (sizeof(Boot_data_t) % 4)
    {
        write_size++;
    }

    p_boot_data->boot_data_crc = 0xFFFFFFFF;
    p_boot_data->boot_data_crc = Boot_CRC32((const uint8_t *)p_boot_data, sizeof(Boot_data_t));
    printfHex((uint8_t *)p_boot_data, sizeof(Boot_data_t));
    if (STMFLASH_Write(BOOT_DATA_ADDR, (uint32_t *)p_boot_data, write_size) != true)
    {
        printf("Write boot data to flash failed. \r\n");
        return false;
    }

    Boot_ReadData(&temp_data);
    if (temp_data.boot_data_crc != p_boot_data->boot_data_crc)
    {
        printf("Write boot data verified failed. \r\n");
        return false;
    }

    return true;
}

/**
 * @function: Boot_UpdataToFlash
 * @brief This function is used to write the firmware to the flash.
 * @param {uint32_t} flash_addr: Flash address
 * @param {uint32_t} data_size: Data size. Unit: Byte
 * @return {bool} true: Write success, false: Write failed.
 */
static bool Boot_WirteFirmwareToFlash(uint32_t flash_addr, uint32_t firmware_addr, uint32_t firmware_size)
{
    // uint8_t md5_result[16] = {0};
    uint16_t set_readsize = 0;
    uint32_t pos = 0;

    while (pos < firmware_size)
    {
        set_readsize = ((firmware_size - pos) > sizeof(firmware_buff)) ? (sizeof(firmware_buff)) : (firmware_size - pos);

        if (BSP_W25Qx_Read(firmware_buff, firmware_addr + pos, set_readsize) != true)
        {
            printf("Read firmware failed from w25q32! \r\n");
            return false;
        }
        if (STMFLASH_WriteByte(flash_addr + pos, firmware_buff, set_readsize) != true)
        {
            printf("Write APP Error!!! \r\n");
            return false;
        }
        pos += set_readsize;
    }
    return true;
}

/**
 * @function: Boot_JumpFlagCheck
 * @brief This function is used to check the jump flag
 * @return {void}
 */
void Boot_JumpFlagCheck(void)
{
    if (Boot_ReadData(&boot_data) != true)
    {
        return;
    }
    if (boot_data.boot_flag & BOOT_FLAG_UPDATED_APP)
    {
        Boot_ExecuteApp(BOOT_APP_START_ADDR);
    }
}

/**
 * @function: Boot_UpdataFlagCheck
 * @brief This function is used to check the update flag
 * @return {*}
 */
void Boot_UpdataFlagCheck(void)
{
    if (Boot_ReadData(&boot_data) != true)
    {
        printf("Read boot data failed. \r\n");
        boot_data.boot_flag = 0;
    }
    printf("boot_flag: [%d], firmware_crc32: [%d], firmware_size: [%d], boot_data_crc: [0x%08X] \r\n",
        boot_data.boot_flag, boot_data.firmware_crc32, boot_data.firmware_size, boot_data.boot_data_crc);

    if (boot_data.boot_flag & BOOT_FLAG_UPDATE_APP_REQUEST)
    {
        if ((boot_data.firmware_size != 0) && (Boot_WirteFirmwareToFlash(BOOT_APP_START_ADDR, BOOT_FIRMWARE_ADDR, boot_data.firmware_size) == true))
        {
            boot_data.boot_flag = BOOT_FLAG_UPDATED_APP;
            if (Boot_WriteData(&boot_data) == true)
            {
                printf("OTA successfully! \r\n");
                HAL_NVIC_SystemReset();
            }
            printf("Write boot data feiled! \r\n");
        }
    }
    else
    {
        printf("Boot flag error... \r\n");
        HAL_Delay(2000);
        boot_data.boot_flag = BOOT_FLAG_UPDATED_APP;
        if (Boot_WriteData(&boot_data) == true)
        {
            printf("Try run application... \r\n");
            HAL_NVIC_SystemReset();
        }
        printf("Write boot data feiled! \r\n");
    }
}


